package com.google.gwt.gwtpages.client.page;

import java.util.HashMap;
import java.util.Map;

import com.google.gwt.gwtpages.client.GWTPagesSettings;
import com.google.gwt.gwtpages.client.page.parameters.PageParameters;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * Application presenter which can use several different application presenters based on the page
 * or session attributes.  Each known presenter is mapped to a String token and all available presenters
 * are passed as entries into the constructor.
 * 
 * To change the presenter per page, you can either
 * <ul>
 * 	<li>add a {@link Oracle} to the {@link GWTPageAttributes} in a page loader using {@link GWTPageAttributes#put(Class, Object)}
 * 		with the Class parameter being {@link CompositeApplicationPresenter.Oracle}</li>
 * 	<li>add a {@link Oracle} to the {@link PageRequestSession} in a page request event using {@link GWTPageAttributes#put(Class, Object)}
 * 		with the Class parameter being {@link CompositeApplicationPresenter.Oracle}</li>
 * </ul>
 * 
 * If no Oracle is set, the default application presenter will be used.
 * 
 * @author Joe Hudson
 */
public abstract class CompositeApplicationPresenter extends SimplePanel implements
		GWTPagesApplicationPresenter {

	protected Map<String, GWTPagesApplicationPresenter> index;
	protected GWTPagesApplicationPresenter currentPresenter;
	
	public CompositeApplicationPresenter(Entry... entries) {
		index = new HashMap<String, GWTPagesApplicationPresenter>();
		for (Entry entry : entries) {
			index.put(entry.token, entry.presenter);
		}
	}

	public void init(GWTPagesSettings settings) {
		for (GWTPagesApplicationPresenter presenter : index.values())
			presenter.init(settings);
	}

	public void showPage(GWTPage page, PageParameters parameters,
			PageRequestSession session) {
		Oracle oracle = session.get(Oracle.class);
		GWTPagesApplicationPresenter presenter = null;
		String presenterToken = null;
		if (null == oracle) presenterToken = getDefaultApplicationPresenterToken();
		else presenterToken = oracle.getPresenterToken(page, parameters, session);
		if (null != presenterToken) {
			presenter = index.get(getDefaultApplicationPresenterToken());
		}
		if (null == presenter) {
			if (null == presenterToken) throw new NullPointerException("Null presenter token for page '" + page.getPageToken() + "'");
			else throw new NullPointerException("No presenter located for token '" + presenterToken + "'");
		}
		if (null == currentPresenter || !currentPresenter.equals(presenter)) {
			clear();
			add(presenter.asWidget());
			currentPresenter = presenter;
		}
		presenter.showPage(page, parameters, session);
	}

	public Widget asWidget() {
		return this;
	}

	public abstract String getDefaultApplicationPresenterToken();

	public class Entry {
		private String token;
		private CompositeApplicationPresenter presenter;
		
		public Entry (String token, CompositeApplicationPresenter presenter) {
			this.token = token;
			this.presenter = presenter;
		}
	}
	
	public interface Oracle {
		public String getPresenterToken(GWTPage page, PageParameters parameters,
				PageRequestSession session);
	}
}
